/* what is this ? 
NAME    Simple self-defined communication protocol
DESCRIPTION
   本文件组织了一份简单的自定义通信协议，支持
   - 端口校验：目的端口2B，源端口2B
   - 长度读取：2B
   - 校验和：1B
   - 保留位：1B
   （以上功能除长度外在本文件中一概不支持）
   (没有最后一条消息的确认,这下不得不支持保留位了)
   */

/* header files */
#include "protocol.h"
/* function declaration */
int scpsend(int fd, const void* buf, size_t msglen, int flags);
int scprecv(int fd, void* buf, size_t msglen, int flags);











int scpsend(int fd, const void* buf, size_t msglen, int flags)
{// 函数接收长度为msglen的信息并分割为包括首部在内最大长度SCPMSG_SIZE的信息

    int sendcheck;

    // 计算分割次数
    int partition = msglen / SCPMSG_DATASIZE;

    // 计算最后一次发送信息数据部分长度
    int lastmsg_datalen = msglen % SCPMSG_DATASIZE;

    // 拼接首部并发送
    char buffer[SCPMSG_SIZE];   // SCP发信缓冲区
    const char* start = (const char*)buf;   // start指示从buf的何处开始读取
    SCP_header_t header;
    memset(&header, 0, sizeof(header));
    scpdatalen_t scpmsg_datalen;

    for(int i=0; i<partition; i++){
        scpmsg_datalen = SCPMSG_DATASIZE;    // 满载
        memcpy(&header.len, &scpmsg_datalen, sizeof(scpmsg_datalen));     // 拼接首部
        memcpy(buffer, &header, SCPMSG_HEADERSIZE);

        memcpy(buffer + SCPMSG_HEADERSIZE, start, scpmsg_datalen);   // 将消息写入缓冲区
        sendcheck = send(fd, buffer, SCPMSG_SIZE, flags);   // 满载发送
        assert(sendcheck == SCPMSG_SIZE);

        start += SCPMSG_DATASIZE;   // 下次拼接起始位置
    }

    // 最后一次发送 
    scpmsg_datalen = lastmsg_datalen;
    memcpy(&header.len, &scpmsg_datalen, sizeof(scpmsg_datalen));     // 拼接首部
    
    memcpy(buffer, &header, SCPMSG_HEADERSIZE);
    uint8_t lastflag = 128;
    memcpy(&header.reserve, &lastflag, sizeof(lastflag));   // 最后一条消息
    memcpy(buffer + SCPMSG_HEADERSIZE, start, lastmsg_datalen);   // 将消息写入缓冲区
    
    sendcheck = send(fd, buffer, 
                     lastmsg_datalen + SCPMSG_HEADERSIZE, 
                     flags);
    assert(sendcheck == lastmsg_datalen + SCPMSG_HEADERSIZE);


    return msglen;
}


int scprecv(int fd, void* buf, size_t msglen, int flags)
{// 接收首部消息 && 接收内含长度消息 && 返回读取字节数
 // 遗憾的是，msglen此时无法作为接收方读取字节数
 // 此处为形式上的一致，规定msglen为缓冲区buf字节数
 // 如msglen太小直接返回
    SCP_header_t header;
    recv(fd, &header, SCPMSG_HEADERSIZE, flags);

    uint8_t lastflag;
    memcpy(&lastflag, &header.reserve, sizeof(header.reserve));
    if(lastflag & LASTMESSAGE){ return -2;}

    scpdatalen_t datalen;
    memcpy(&datalen, &header.len, sizeof(datalen));

    assert(msglen >= datalen);

    recv(fd, buf, datalen, flags);

    return datalen;
}


